package com.zrq.secondkill.service.impl;

import com.zrq.secondkill.constant.Constant;
import com.zrq.secondkill.dao.ItemDOMapper;
import com.zrq.secondkill.dao.ItemStockDOMapper;
import com.zrq.secondkill.dao.PromoDOMapper;
import com.zrq.secondkill.dao.StockLogDOMapper;
import com.zrq.secondkill.dataobject.ItemDO;
import com.zrq.secondkill.dataobject.ItemStockDO;
import com.zrq.secondkill.dataobject.PromoDO;
import com.zrq.secondkill.dataobject.StockLogDO;
import com.zrq.secondkill.enums.OrderStatusEnum;
import com.zrq.secondkill.enums.PromoStatusEnum;
import com.zrq.secondkill.service.ItemService;
import com.zrq.secondkill.service.SimplelayeredCacheService;
import com.zrq.secondkill.service.UniqueIdService;
import com.zrq.secondkill.service.model.ItemModel;
import com.zrq.secondkill.service.model.PromoModel;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {

    final
    ItemDOMapper itemDOMapper;

    final
    PromoDOMapper promoDOMapper;

    final
    ItemStockDOMapper itemStockDOMapper;

    final
    StockLogDOMapper stockLogDOMapper;

    final
    RedisTemplate redisTemplate;

    final
    UniqueIdService uniqueIdService;


    final
    SimplelayeredCacheService simplelayeredCacheService;

    private static final String REDIS_ITEM_CACHE_PREFIX = "itemCache";

    public ItemServiceImpl(ItemDOMapper itemDOMapper, PromoDOMapper promoDOMapper, ItemStockDOMapper itemStockDOMapper, StockLogDOMapper stockLogDOMapper, RedisTemplate redisTemplate, UniqueIdService uniqueIdService, SimplelayeredCacheService simplelayeredCacheService) {
        this.itemDOMapper = itemDOMapper;
        this.promoDOMapper = promoDOMapper;
        this.itemStockDOMapper = itemStockDOMapper;
        this.stockLogDOMapper = stockLogDOMapper;
        this.redisTemplate = redisTemplate;
        this.uniqueIdService = uniqueIdService;
        this.simplelayeredCacheService = simplelayeredCacheService;
    }

    /**
     * TODO(zrq) 目前返回的是所有商品的列表,之后再决定返回商品还是只返回促销商品
     * TODO(zrq) 返回的商品没有促销信息,延后处理
     */
    @Override
    public List<ItemModel> listItem() {

        List<ItemDO> itemDOs = itemDOMapper.listItem();

        List<ItemModel> itemModels = itemDOs.stream()
                .map(ItemServiceImpl::convertItemModelFromItemDO)
                .collect(Collectors.toList());

        return itemModels;
    }

    /**
     * 先查本地热点缓存和redis,没有则查数据库
     *
     * @param id 商品id
     * @return
     */
    @Override
    public ItemModel getItemByIdInCache(Integer id) {
        String cacheKey = REDIS_ITEM_CACHE_PREFIX + id;

        ItemModel itemModel = (ItemModel) simplelayeredCacheService.get(cacheKey);
        if (itemModel == null) {
            ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
            itemModel = convertItemModelFromItemDO(itemDO);

            PromoDO promoDO = promoDOMapper.selectByItemId(itemDO.getId());
            PromoModel promoModel = convertPromoModelFromPromoDO(promoDO);
            itemModel.setPromoModel(promoModel);

            ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(id);
            itemModel.setStock(itemStockDO.getStock());

            simplelayeredCacheService.setK(cacheKey, itemModel, 1, TimeUnit.MINUTES, 2);

        }
        return itemModel;
    }


//    @Override
//    public Boolean decreaseStock(Integer itemId, Integer amount) {
//        int affectedRow = itemStockDOMapper.updateByItemIdWhereItemIdGreaterThanZero(itemId, -amount);
//        return affectedRow > 0;
//    }

    /**
     * 若库存足够,扣减redis中的库存,如果扣减后商品库存为0,在redis中打上售罄标识
     *
     * @param itemId
     * @param amount
     * @return 操作是否成功
     */
    @Override
    public Boolean decreaseStockInRedisIfEnough(Integer itemId, Integer amount) {
        String cacheKey = Constant.STOCK_CACHE_PREFIX + itemId;

        long stock = redisTemplate.opsForValue().increment(cacheKey, amount * (-1));

        if (stock >= 0) {
            //打售罄标识
            if (stock == 0) {
                redisTemplate.opsForValue().set(Constant.SOLD_OUT_PREFIX + itemId, 1);
            }
            return true;
        } else {
            redisTemplate.opsForValue().increment(cacheKey, amount);
            return false;
        }
    }

    @Override
    public Boolean decreaseStockInDatabaseIfEnough(Integer itemId, Integer amount) {
        int affectedRow = itemStockDOMapper.decreaseStockIfEnough(itemId, amount);
        return affectedRow > 0;
    }

    /**
     * 在订单日志中创建一条记录
     *
     * @return stockLogId
     */
    @Override
    @Transactional(rollbackFor = {Exception.class,RuntimeException.class})
    public String initStockLog(Integer itemId, Integer amount) {

        StockLogDO stockLogDO = new StockLogDO();
        stockLogDO.setItemId(itemId);
        stockLogDO.setAmount(amount);
        stockLogDO.setStatus(OrderStatusEnum.UNCOMMITTED.getStatus());

        String stockLogId = uniqueIdService.generateUniqueId();
        stockLogDO.setStockLogId(stockLogId);

        stockLogDOMapper.insert(stockLogDO);

        return stockLogId;
    }

    @Override
    public Integer getItemStockInCache(Integer itemId) {
        return (Integer) redisTemplate.opsForValue().get(Constant.STOCK_CACHE_PREFIX+itemId);
    }

    private static ItemModel convertItemModelFromItemDO(ItemDO itemDO) {
        ItemModel itemModel = new ItemModel();
        BeanUtils.copyProperties(itemDO, itemModel);
        //TODO(zrq):精度问题应该如何解决,如何在所有层次都一致的使用BigDecimal
        itemModel.setPrice(BigDecimal.valueOf(itemDO.getPrice()));
        return itemModel;
    }

    private static PromoModel convertPromoModelFromPromoDO(PromoDO promoDO) {
        PromoModel promoModel = new PromoModel();
        BeanUtils.copyProperties(promoDO, promoModel);

        if (promoDO.getStartDate().toInstant().isAfter(Instant.now())) {
            promoModel.setStatus(PromoStatusEnum.HAVE_NOT_BEGIN);
        } else if (promoDO.getEndDate().toInstant().isBefore(Instant.now())) {
            promoModel.setStatus(PromoStatusEnum.END);
        } else {
            promoModel.setStatus(PromoStatusEnum.ON_GOING);
        }

        promoModel.setStartDate(new DateTime(promoDO.getStartDate()));
        promoModel.setEndDate(new DateTime(promoDO.getEndDate()));

        promoModel.setPromoItemPrice(BigDecimal.valueOf(promoDO.getPromoItemPrice()));
        return promoModel;
    }
}
